import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from flask import Flask, render_template, request, jsonify, make_response
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from io import BytesIO, StringIO
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from functools import lru_cache
import logging
# 注册中文字体
# 这里假设你已经有了支持中文的字体文件，如SimHei.ttf
# 可以从系统字体目录中复制，Windows通常在C:\Windows\Fonts\
try:
    # 注册黑体字体，支持中文显示
    pdfmetrics.registerFont(TTFont('SimHei', 'SimHei.ttf'))
except:
    # 如果找不到指定字体，尝试注册其他常见中文字体
    try:
        pdfmetrics.registerFont(TTFont('Microsoft YaHei', 'msyh.ttc'))
    except:
        print("警告：未找到中文字体，PDF中的中文可能无法正常显示")


class Dashboard:
    def __init__(self, data_processor, behavior_analyzer, rfm_analyzer, prediction_model):
        self.data_processor = data_processor
        self.behavior_analyzer = behavior_analyzer
        self.rfm_analyzer = rfm_analyzer
        self.prediction_model = prediction_model

        self.app = Flask(__name__)
        self._setup_routes()

    def _setup_routes(self):
        """设置Flask路由"""

        @self.app.route('/')
        def index():
            return render_template('dashboard.html')

        @self.app.route('/rfm_segmentation')
        def rfm_segmentation():
            return render_template('rfm_segmentation.html')

        @self.app.route('/prediction_monitoring')
        def prediction_monitoring():
            return render_template('prediction_monitoring.html')

        @self.app.route('/api/daily_visits')
        def get_daily_visits():
            daily_data = self.data_processor.get_daily_visits()
            return jsonify(daily_data.to_dict(orient='records'))

        @self.app.route('/api/cumulative_purchases')
        def get_cumulative_purchases():
            months = list(map(int, request.args.get('months', '11,12').split(',')))
            year = int(request.args.get('year', 2017))
            purchases_data = self.data_processor.get_cumulative_purchases(months, year)

            result = {}
            for key, value in purchases_data.items():
                if isinstance(value, pd.DataFrame):
                    result[key] = {
                        'daily_data': value.to_dict(orient='records'),
                        'monthly_total': int(value['购买量'].sum())  # 将numpy.int64转换为int
                    }
                elif isinstance(value, np.int64):
                    result[key] = int(value)  # 将numpy.int64转换为int

            return jsonify(result)

        @self.app.route('/api/category_ranking')
        def get_category_ranking():
            year = int(request.args.get('year', 2017))
            month = int(request.args.get('month', 11))
            ranking_data = self.data_processor.get_category_purchase_ranking(year, month)
            return jsonify(ranking_data.to_dict(orient='records'))

        @self.app.route('/api/top_purchase_users')
        def get_top_purchase_users():
            percentage = int(request.args.get('percentage', 30))
            top_users = self.data_processor.get_top_purchase_users(percentage)
            return jsonify(top_users.to_dict(orient='records'))

        @self.app.route('/api/behavior_distribution')
        def get_behavior_distribution():
            distribution = self.behavior_analyzer.get_behavior_distribution()
            return jsonify(distribution.to_dict(orient='records'))

        @self.app.route('/api/user_active_time')
        def get_user_active_time():
            hourly, weekday = self.behavior_analyzer.get_user_active_time()
            return jsonify({
                'hourly': hourly.to_dict(orient='records'),
                'weekday': weekday.to_dict(orient='records')
            })

        @self.app.route('/api/association_rules')
        def get_association_rules():
            min_support = float(request.args.get('min_support', 0.01))
            min_confidence = float(request.args.get('min_confidence', 0.5))
            rules = self.behavior_analyzer.find_item_association_rules(min_support, min_confidence)
            return jsonify(rules.to_dict(orient='records'))

        @self.app.route('/api/rfm_scores')
        def get_rfm_scores():
            # 调用 RFMAnalyzer 的 calculate_rfm 方法获取 RFM 指标数据
            rfm_scores = self.rfm_analyzer.calculate_rfm()
            # 将 DataFrame 转换为字典并返回 JSON 格式
            return jsonify(rfm_scores.to_dict(orient='records'))

        @self.app.route('/api/rfm_clusters')
        def get_rfm_clusters():
            try:
                n_clusters = int(request.args.get('n_clusters', 3))
                # segment_rfm返回的是包含多个数据的字典
                cluster_result = self.rfm_analyzer.segment_rfm(n_clusters)
                # 我们需要返回rfm_data部分，它包含用户的聚类信息
                return jsonify(cluster_result['rfm_data'].to_dict(orient='records'))
            except Exception as e:
                # 添加错误处理，便于调试
                return jsonify({'error': str(e)}), 500

        # 在_setup_routes方法中添加
        # 在dashboard.py的_setup_routes方法中修改get_user_timeline路由
        @self.app.route('/api/user_timeline')
        def get_user_timeline():
            try:
                # 验证用户ID参数
                user_id_str = request.args.get('user_id')
                if not user_id_str or not user_id_str.isdigit():
                    return jsonify({'error': '请提供有效的用户ID（数字）'}), 400

                user_id = int(user_id_str)

                # 获取用户行为时间线数据
                timeline = self.behavior_analyzer.get_user_behavior_timeline(user_id)

                if timeline.empty:
                    logging.info(f'未找到用户ID为{user_id}的行为记录')
                    return jsonify({
                        'message': f'未找到用户ID为{user_id}的行为记录',
                        'data': []
                    })

                # 转换datetime为字符串格式，避免JSON序列化问题
                timeline['datetime'] = timeline['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')

                # 转换为字典并返回
                return jsonify({
                    'data': timeline.to_dict(orient='records'),
                    'count': len(timeline)
                })
            except Exception as e:
                logging.error(f'获取用户时间线时发生错误: {str(e)}', exc_info=True)
                return jsonify({'error': f'服务器错误: {str(e)}'}), 500

        @self.app.route('/api/user_segments')
        def get_user_segments():
            n_clusters = int(request.args.get('n_clusters', 3))  # 从请求参数获取聚类数量
            segments = self.rfm_analyzer.get_user_segments(n_clusters)
            return jsonify(segments.to_dict(orient='records'))

        # 新增用户画像API
        @self.app.route('/api/user_portrait')
        def get_user_portrait():
            try:
                n_clusters = int(request.args.get('n_clusters', 3))
                portrait_data = self.rfm_analyzer.analyze_user_portrait(n_clusters)
                return jsonify(portrait_data)
            except Exception as e:
                return jsonify({'error': str(e)}), 500

        @self.app.route('/api/purchase_intention')
        def get_purchase_intention():
            # 检查模型是否已训练，如未训练则训练，已训练则直接使用缓存结果
            if hasattr(self.prediction_model, 'purchase_intention_ranking'):
                ranking = self.prediction_model.purchase_intention_ranking
            else:
                ranking = self.prediction_model.train_purchase_intention_model()
                # 保存结果到模型实例，避免重复训练
                self.prediction_model.purchase_intention_ranking = ranking
            return jsonify(ranking.to_dict(orient='records'))

        @self.app.route('/api/recommendations')
        def get_recommendations():
            user_id = int(request.args.get('user_id', 0))
            num_recs = int(request.args.get('num_recs', 10))

            if user_id == 0:
                # 如果没有提供用户ID，返回默认推荐
                return jsonify([])

            recs = self.prediction_model.get_recommendations(user_id, num_recs)
            return jsonify(recs.to_dict(orient='records'))

        @self.app.route('/api/download_purchase_ranking')
        def download_purchase_ranking():
            try:
                # 获取购买意向排名数据
                if hasattr(self.prediction_model, 'purchase_intention_ranking'):
                    ranking = self.prediction_model.purchase_intention_ranking
                else:
                    ranking = self.prediction_model.train_purchase_intention_model()
                    self.prediction_model.purchase_intention_ranking = ranking

                # 创建PDF
                buffer = BytesIO()
                doc = SimpleDocTemplate(buffer, pagesize=letter)

                # 创建支持中文的样式
                styles = getSampleStyleSheet()
                # 为标题创建支持中文的样式
                styles.add(ParagraphStyle(
                    name='ChineseTitle',
                    parent=styles['Title'],
                    fontName='SimHei' if 'SimHei' in pdfmetrics.getRegisteredFontNames() else
                    'Microsoft YaHei' if 'Microsoft YaHei' in pdfmetrics.getRegisteredFontNames() else 'Helvetica',
                    fontSize=18,
                    spaceAfter=12
                ))
                # 为表格内容创建支持中文的样式
                styles.add(ParagraphStyle(
                    name='ChineseNormal',
                    parent=styles['Normal'],
                    fontName='SimHei' if 'SimHei' in pdfmetrics.getRegisteredFontNames() else
                    'Microsoft YaHei' if 'Microsoft YaHei' in pdfmetrics.getRegisteredFontNames() else 'Helvetica',
                    fontSize=10
                ))

                # 添加标题
                elements = []
                title = Paragraph("用户购买概率排行榜", styles['ChineseTitle'])
                elements.append(title)
                elements.append(Spacer(1, 12))

                # 添加表格数据
                # 准备表格数据，先添加表头
                table_data = [
                    [Paragraph('排名', styles['ChineseNormal']),
                     Paragraph('用户ID', styles['ChineseNormal']),
                     Paragraph('推荐商品ID', styles['ChineseNormal']),
                     Paragraph('购买概率(%)', styles['ChineseNormal'])]
                ]

                # 获取去重后的前100位用户
                seen_user_ids = set()
                unique_users = []
                for item in ranking.itertuples(index=False):
                    if item.user_id not in seen_user_ids:
                        seen_user_ids.add(item.user_id)
                        unique_users.append(item)
                        if len(unique_users) >= 100:
                            break

                # 添加数据行
                for i, item in enumerate(unique_users, 1):
                    probability = getattr(item, '购买概率', 0) * 100 if getattr(item, '购买概率', 0) else 0
                    table_data.append([
                        Paragraph(str(i), styles['ChineseNormal']),
                        Paragraph(str(item.user_id), styles['ChineseNormal']),
                        Paragraph(str(item.item_id), styles['ChineseNormal']),
                        Paragraph(f"{probability:.4f}", styles['ChineseNormal'])
                    ])

                # 创建表格并设置样式
                table = Table(table_data)
                style = TableStyle([
                    ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
                    ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
                    ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                    ('FONTNAME', (0, 0), (-1, -1), 'SimHei' if 'SimHei' in pdfmetrics.getRegisteredFontNames() else
                    'Microsoft YaHei' if 'Microsoft YaHei' in pdfmetrics.getRegisteredFontNames() else 'Helvetica'),
                    ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
                    ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
                    ('GRID', (0, 0), (-1, -1), 1, colors.black),
                    ('FONTSIZE', (0, 0), (-1, -1), 10),
                ])
                table.setStyle(style)

                # 添加表格到文档
                elements.append(table)

                # 构建PDF
                doc.build(elements)

                # 准备响应
                buffer.seek(0)
                response = make_response(buffer.getvalue())
                response.headers['Content-Type'] = 'application/pdf'
                response.headers['Content-Disposition'] = 'attachment; filename="purchase_probability_ranking.pdf"'
                return response
            except Exception as e:
                return jsonify({'error': str(e)}), 500

        # 修改dashboard.py中的用户列表API
        @self.app.route('/api/user_list')
        def get_user_list():
            try:
                # 添加参数验证和默认值
                n_clusters = request.args.get('n_clusters', 3)
                date_range = request.args.get('date_range', '180')

                # 验证n_clusters是有效的整数
                try:
                    n_clusters = int(n_clusters)
                    if n_clusters < 2 or n_clusters > 10:
                        return jsonify({'error': 'n_clusters必须在2到10之间'}), 400
                except ValueError:
                    return jsonify({'error': 'n_clusters必须是整数'}), 400

                # 验证date_range
                valid_ranges = ['30', '90', '180', '365', 'all']
                if date_range not in valid_ranges and not date_range.isdigit():
                    return jsonify({'error': f'date_range必须是{valid_ranges}之一或数字'}), 400

                # 获取用户列表数据
                user_list = self.rfm_analyzer.get_user_list(n_clusters, date_range)

                return jsonify(user_list)
            except Exception as e:
                # 记录详细错误信息
                self.logger.error(f"获取用户列表失败: {str(e)}", exc_info=True)
                return jsonify({'error': '获取用户列表失败', 'details': str(e)}), 500

    def run(self, host='localhost', port=3000, debug=False):
        """运行Flask应用"""
        self.app.run(host=host, port=port, debug=debug)
